﻿/*********************************************************************************
*
* Copyright (c) ReflectSoftware, Inc. All rights reserved. 
*
* See License.rtf in the solution root for license information.
*
**********************************************************************************/

using System;
using System.Text;

using log4net.Core;
using log4net.Appender;

using ReflectSoftware.Insight;
using ReflectSoftware.Insight.Common;

using RI.Utils.ExceptionManagement;

namespace ReflectSoftware.Insight.Extensions.Log4net
{
    /// <summary>
    /// Redirect all log4net messages to ReflectInsight
    /// </summary>
    public class LogAppender : AppenderSkeleton
    {
        private readonly static String FLine;

        private ReflectInsight FReflectInsight;
        private Boolean FDisplayLevel;
        private Boolean FDisplayLocation;
        
        protected String InstanceName { get; set; }
        protected String DisplayLevel { get; set; }
        protected String DisplayLocation { get; set; }

        //---------------------------------------------------------------------
        /// <summary>
        /// Initializes the <see cref="LogAppender"/> class.
        /// </summary>
        static LogAppender()
        {
            FLine = String.Format("{0,40}", String.Empty).Replace(" ", "-");
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Initializes a new instance of the <see cref="LogAppender"/> class.
        /// </summary>
        public LogAppender()
        {
            InstanceName = String.Empty;
            DisplayLevel = String.Empty;
            DisplayLocation = String.Empty;
            FReflectInsight = null;
            RIEventManager.OnServiceConfigChange += DoOnConfigChange;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Called when [close].
        /// </summary>
        protected override void OnClose()
        {
            RIEventManager.OnServiceConfigChange -= DoOnConfigChange;
            base.OnClose();
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Does the on config change.
        /// </summary>
        private void DoOnConfigChange()
        {
            OnConfigChange();
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Activates the options.
        /// </summary>
        public override void ActivateOptions()
        {
            base.ActivateOptions();
            OnConfigChange();
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Called when [config change].
        /// </summary>
        private void OnConfigChange()
        {
            try
            {
                lock (this)
                {
                    FReflectInsight = RILogManager.Get(InstanceName) ?? RILogManager.Default;
                    
                    FDisplayLevel = DisplayLevel.ToLower().Trim() == "true";
                    FDisplayLocation = DisplayLocation.ToLower().Trim() == "true"; 
                }
            }
            catch (Exception ex)
            {
                RIExceptionManager.Publish(ex, "Failed during: LogAppender.OnConfigChange()");
            }
        }

        //--------------------------------------------------------------------- 
        /// <summary>
        /// Sends the message.
        /// </summary>
        /// <param name="mType">Type of the m.</param>
        /// <param name="loggingEvent">The logging event.</param>
        private void SendMessage(MessageType mType, LoggingEvent loggingEvent)
        {
            // build details
            StringBuilder sb = null;  

            if (loggingEvent.ExceptionObject != null)
            {
                sb = new StringBuilder();
                sb.Append(ExceptionBasePublisher.ConstructIndentedMessage(loggingEvent.ExceptionObject));
                sb.AppendLine();
                sb.AppendLine();
            }

            if (FDisplayLevel || FDisplayLocation)
            {
                sb = sb ?? new StringBuilder();

                sb.AppendLine("Log4net Details:");
                sb.AppendLine(FLine);

                if (FDisplayLevel)
                    sb.AppendFormat("{0,10}: {1}{2}", "Level", loggingEvent.Level.DisplayName, Environment.NewLine);

                if (FDisplayLocation)
                {
                    sb.AppendFormat("{0,10}: {1}{2}", "ClassName", loggingEvent.LocationInformation.ClassName, Environment.NewLine);
                    sb.AppendFormat("{0,10}: {1}{2}", "MethodName", loggingEvent.LocationInformation.MethodName, Environment.NewLine);
                    sb.AppendFormat("{0,10}: {1}{2}", "FileName", loggingEvent.LocationInformation.FileName, Environment.NewLine);
                    sb.AppendFormat("{0,10}: {1}{2}", "LineNumber", loggingEvent.LocationInformation.LineNumber, Environment.NewLine);
                    sb.AppendFormat("{0,10}: {1}{2}", "FullInfo", loggingEvent.LocationInformation.FullInfo, Environment.NewLine);
                }
            }

            String details = sb != null ? sb.ToString() : null;
            FReflectInsight.Send(mType, loggingEvent.RenderedMessage, details); 
        }

        //--------------------------------------------------------------------- 
        /// <summary>
        /// Appends the specified logging event.
        /// </summary>
        /// <param name="loggingEvent">The logging event.</param>
        protected override void Append(LoggingEvent loggingEvent)
        {
            lock (this)
            {                
                if (loggingEvent.Level == Level.Info)
                {
                    if (loggingEvent.RenderedMessage.StartsWith("[Enter]"))
                    {
                        FReflectInsight.EnterMethod(loggingEvent.RenderedMessage.Replace("[Enter]", String.Empty));
                        return;
                    }
                    if (loggingEvent.RenderedMessage.StartsWith("[Exit]"))
                    {                     
                        FReflectInsight.ExitMethod(loggingEvent.RenderedMessage.Replace("[Exit]", String.Empty));
                        return;
                    }

                    SendMessage(MessageType.SendInformation, loggingEvent);                    
                }
                else if (loggingEvent.Level == Level.Trace)
                {
                    SendMessage(MessageType.SendTrace, loggingEvent);
                }
                else if (loggingEvent.Level == Level.Debug 
                     || loggingEvent.Level == Level.Log4Net_Debug)
                {
                    SendMessage(MessageType.SendDebug, loggingEvent);
                }
                else if (loggingEvent.Level == Level.Warn)
                {
                    SendMessage(MessageType.SendWarning, loggingEvent);
                }
                else if (loggingEvent.Level == Level.Error
                    || loggingEvent.Level == Level.Alert                    
                    || loggingEvent.Level == Level.Emergency
                    || loggingEvent.Level == Level.Severe)
                {
                    SendMessage(MessageType.SendError, loggingEvent);
                }
                else if (loggingEvent.Level == Level.Fatal
                    || loggingEvent.Level == Level.Critical)
                {
                    SendMessage(MessageType.SendFatal, loggingEvent);
                }
                else if (loggingEvent.Level == Level.Notice)
                {
                    SendMessage(MessageType.SendNote, loggingEvent);
                }
                else if (loggingEvent.Level == Level.Verbose)
                {
                    SendMessage(MessageType.SendVerbose, loggingEvent);
                }
                else // safetynet
                {
                    SendMessage(MessageType.SendMsg, loggingEvent);
                }
            }
        }

        //--------------------------------------------------------------------- 
        /// <summary>
        /// Gets a value indicating whether [requires layout].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [requires layout]; otherwise, <c>false</c>.
        /// </value>
        protected override bool RequiresLayout
        {
            get { return false; }
        }
    }
}
